#include	"a_global_vars.h"

int PACK_ID = 0, nextProp = 0, propLIMIT = -1;

struct timeval last, current, spent;
//------------------------------------------------------------------------------
void instance_builder(proposer_msg mm) {
	bool allowed = true;
	int i = a_interval_performance->next_instance - 1;
	//**************************PREVENT OVERWRITING*****************************			
	if (((i - a_interval_performance->highest_deliverable_inst_id) == INTERNAL_BUFFER_SIZE))
		allowed = false;
	else
		assert(i - a_interval_performance->highest_deliverable_inst_id <= INTERNAL_BUFFER_SIZE);
	//**************************PREVENT OVERWRITING*****************************
	if (allowed) {
		memcpy(&data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].packet.msgs[0], &mm, sizeof(proposer_msg));
		//		msg_counter++;
		//		if (msg_counter == MAX_MSG_COUNT) {
		//			msg_counter = 0;
		propLIMIT = a_interval_performance->next_instance;
		PACK_ID++;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].packet.pack_id = PACK_ID;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].deliver = false;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].received = false;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].instance_id = a_interval_performance->next_instance;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].coordinator_id = COORDINATOR_ID;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].dec = -1;
		data[(a_interval_performance->next_instance % INTERNAL_BUFFER_SIZE)].round_number = 1;// =0
		a_interval_performance->next_instance++;
		a_interval_performance->total_num_instances++;
		a_interval_performance->interval_num_packets_proposer_to_multicaster++;
		//		}
	}

}
//------------------------------------------------------------------------------
void get_msg(int sd, short event, void * arg) {
	UNUSED_ARG(event);
	UNUSED_ARG(arg);

	int status = 0;
	struct sockaddr_in c_addr;
	unsigned int slen = sizeof(c_addr);
	proposer_msg gm;
	//--------------------------------------------------------------------------
	if (((a_interval_performance->next_instance + 1 - a_interval_performance->highest_deliverable_inst_id) < INTERNAL_BUFFER_SIZE) || (gapEnd - a_interval_performance->highest_deliverable_inst_id) < INTERNAL_BUFFER_SIZE) {

		status = recvfrom(sd, &gm, sizeof(proposer_msg), 0, (struct sockaddr*) &c_addr, &slen);
		assert(status == sizeof(proposer_msg));
		a_interval_performance->interval_bytes_received_from_proposer += status;
		a_interval_performance->interval_proposer_to_multicaster_latency += getMicroseconds() - gm.microseconds;
		instance_builder(gm);

	}
}
//------------------------------------------------------------------------------
void multicast(int nextMulticast) {
	int status;
	status = ipmulti_send(get_config_info()->server_infos[get_config_info()->self_id].multicast_send_sid, &data[nextMulticast], sizeof(inst), ipmAddr);
	assert(status == sizeof(inst));
	a_interval_performance->interval_bytes_multicast += status;
}
//------------------------------------------------------------------------------
int reMulticast(int start, int end, int cnt) {
	int i;
	for (i = start; i < end; i++) {
		if (cnt < SUBMIT_PER_WAKE) {
			if (!data[(i % INTERNAL_BUFFER_SIZE)].deliver && data[(i % INTERNAL_BUFFER_SIZE)].received) {
				data[(i % INTERNAL_BUFFER_SIZE)].dec = -1;
				data[(i % INTERNAL_BUFFER_SIZE)].round_number = 1;// =0
				data[(i % INTERNAL_BUFFER_SIZE)].deliver = false;
				data[(i % INTERNAL_BUFFER_SIZE)].received = false;
				data[(i % INTERNAL_BUFFER_SIZE)].highestDeliverable = a_interval_performance->highest_deliverable_inst_id;

				multicast((i % INTERNAL_BUFFER_SIZE));

				data[(i % INTERNAL_BUFFER_SIZE)].received = true;
				data[(i % INTERNAL_BUFFER_SIZE)].highestDeliverable = -1;

				cnt++;
				a_interval_performance->interval_num_redundant_packets_received++;
				a_interval_performance->total_num_redundant_packets_received++;
			}
		}
	}
	return cnt;
}
//------------------------------------------------------------------------------
void multicast_instance(int sd, short event, void * arg) {

	a_interval_performance->interval_num_entrance++;
	int counter = 0;
	learner_deliver();

	gettimeofday(&current, NULL);
	double msecInterval = measure_time_difference(current, last);

	//*****************************Resubmision**********************************
	if (msecInterval >= 30) {
		if (gapEnd > a_interval_performance->highest_deliverable_inst_id)
			counter = reMulticast(a_interval_performance->highest_deliverable_inst_id + 1, gapEnd + 1, counter);
		else {
			if ((a_interval_performance->highest_deliverable_inst_id == gapEnd) && gapEnd == -1)
				counter = reMulticast(a_interval_performance->highest_deliverable_inst_id + 1, nextProp, counter);
			assert(a_interval_performance->highest_deliverable_inst_id <= gapEnd);
		}
		gettimeofday(&last, NULL);
	}
	//*****************************Resubmision**********************************
	//	if ((gapEnd - a_interval_performance->highest_deliverable_inst_id >= INTERNAL_BUFFER_SIZE) || (nextProp - 1 - a_interval_performance->highest_deliverable_inst_id >= INTERNAL_BUFFER_SIZE))
	//		counter = SUBMIT_PER_WAKE;
	//**************************NEW Multicast***********************************
	//	if (propLIMIT != -1) {
	while (counter < SUBMIT_PER_WAKE && nextProp <= propLIMIT) {
		//			if (nextProp - 1 - a_interval_performance->highest_deliverable_inst_id == INTERNAL_BUFFER_SIZE)
		//				break;
		data[(nextProp % INTERNAL_BUFFER_SIZE)].highestDeliverable = a_interval_performance->highest_deliverable_inst_id;
		multicast((nextProp % INTERNAL_BUFFER_SIZE));
		data[(nextProp % INTERNAL_BUFFER_SIZE)].received = true;

		counter++;
		nextProp++;
	}
	//	}
	//**************************NEW Multicast***********************************
	evtimer_add(multicast_timeout_eve, &tv1);
}
